package com.example.spring.code.a05;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Arrays;

/**
 * BeanFactory 后处理器模拟实现 —— 组件扫描
 */
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(ComponentScanPostProcessor.class);

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            // 1. 获取ComponentScan注解
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                // 2. // 遍历 @ComponentScan 中配置的所有 basePackages 包路径
                for (String basePackage : componentScan.basePackages()) {
                    log.info("扫描包：{}", basePackage);
                    // 3. 构建 classpath* 下的资源路径，格式为：classpath*:com/example/spring/code/**/*.class
                    String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
                    log.info("扫描路径：{}", path);
                    // 4. 使用 Spring 提供的工具类 PathMatchingResourcePatternResolver 查找所有 .class 文件
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    // 创建 Bean 名称生成器（默认使用类名首字母小写）
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    // 创建元数据读取工厂，用于读取类的注解和类信息
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    // 5. 遍历所有找到的 .class 资源文件
                    Arrays.stream(resources).forEach(resource -> {
                        try {
                            // 5.1 获取 MetadataReader，用于读取类的元数据（如类名、注解等）
                            MetadataReader reader = factory.getMetadataReader(resource);
                            // 5.2 获取类的注解元数据
                            AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                            // 5.3 检查该类是否带有 @Component 注解 或者 其组合注解（例如 @Controller、@Service、@Repository 等）
                            if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                                // 5.4 创建 Bean 定义对象，根据类名生成 BeanDefinition
                                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                                // 5.5 生成 Bean 名称，默认为首字母小写
                                String name = generator.generateBeanName(beanDefinition, beanFactory);
                                // 5.6 将 Bean 定义注册到 BeanFactory 中
                                beanFactory.registerBeanDefinition(name, beanDefinition);
                            }
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    });

                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
